//
//  Ezwave.cpp
//  ezLogin
//
//  Created by 刘跃龙 on 2/5/17.
//  Copyright © 2017 absolutemobile. All rights reserved.
//

#include "Ezwave.h"
#if HAVEEZWAVE
#include "platform/Log.h"
#include "EzTools.h"
#include "ABMUtils.h"
#include "AccountData.h"
#include "EzFileSystem.h"

EZ_BEGIN

//#define PTHREAD_COND_INITIALIZER {_PTHREAD_COND_SIG_init, {0}}

using namespace OpenZWave;

OpenZWave::Options* Ezwave::s_Options = NULL;
OpenZWave::Manager* Ezwave::s_Manager = NULL;
std::list<Ezwave*> Ezwave::s_pointList = std::list<Ezwave*>();
std::string Ezwave::s_configFilePath = FileUtils::getInstance()->getWifiConfigPath() + "config";


Ezwave::Ezwave(std::string serial,std::string userPath)
:m_serial(serial)
,m_initFailed(false)
,m_initComplete(false)
{
    m_zwavePath = userPath+"zwave_config/";
    Ez::makeDir(m_zwavePath);
    
    Ezwave::addPointList(this);
    m_initMutex.Lock();
    
    log("Starting MinOZW with OpenZWave Version %s\n", Manager::getVersionAsString().c_str());
    if(!s_Options){
        s_Options = Options::Create( s_configFilePath,m_zwavePath, "" );
        s_Options->AddOptionInt( "SaveLogLevel", LogLevel_None );
        s_Options->AddOptionInt( "QueueLogLevel", LogLevel_None );
        s_Options->AddOptionInt( "DumpTrigger", LogLevel_Error );
        s_Options->AddOptionInt( "PollInterval", 500 );
        s_Options->AddOptionBool( "IntervalBetweenPolls", true );
        s_Options->AddOptionBool("ValidateValueChanges", true);
        s_Options->AddOptionString("NetworkKey", "0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10", false);
        s_Options->Lock();
    }
    
    if(!s_Manager){
        s_Manager = Manager::Create();
    }
    if (s_Manager) {
        s_Manager->AddWatcher(Ezwave::OnNotification,this);
        s_Manager->AddDriver(m_serial, OpenZWave::Driver::ControllerInterface::ControllerInterface_Serial);
    }
    m_initMutex.UnLock();
}

Ezwave::~Ezwave()
{
    if(m_serial.length() > 0) Manager::Get()->RemoveDriver( m_serial );//FIXME 由于在我们的摄像头中m_serial有路径是固定存在的，不保证是有效串口，所以这里判断是否在设备在Manager不起作用，需要其他方式判断，待修改。
    Manager::Get()->RemoveWatcher(Ezwave::OnNotification,this);
    Ezwave::delPointList(this);
    
    for (std::list<Ezwave::NodeInfo*>::iterator iter; iter != m_nodes.end(); iter++) {
        delete (*iter);
    }
    m_nodes.clear();
    m_events.clear();
}

Ezwave::NodeInfo* Ezwave::getNodeInfoFromNodeId(uint8 nodeId)
{
    for( std::list<NodeInfo*>::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it )
    {
        NodeInfo* nodeInfo = *it;
        if( nodeInfo->m_nodeId == nodeId)
        {
            return nodeInfo;
        }
    }
    return NULL;
}

void Ezwave::OnNotification(OpenZWave::Notification const* _notification,void* _context)
{
    Ezwave* self = (Ezwave*)_context;
    if(Ezwave::isExist(self)){
        // Must do this inside a critical section to avoid conflicts with the main thread
        self->m_criticalSection.Lock();
        
        
//        printf("Notification:%s [ type : %d | homeId: %d | nodeId: %d | byte: %d ]\n",Ezwave::getNotificationTypeName(_notification->GetType()).c_str(),_notification->GetType(),_notification->GetHomeId(),_notification->GetNodeId(),_notification->GetByte());
        
        switch( _notification->GetType() )
        {
            case Notification::Type_ValueAdded:
            {
                if( NodeInfo* nodeInfo = self->getNodeInfoFromNodeId(_notification->GetNodeId()))
                {
                    // Add the new value to our list
                    nodeInfo->m_values.push_back( _notification->GetValueID() );
                }
                OpenZWave::Manager::Get()->WriteConfig(self->m_homeId);
                break;
            }
                
            case Notification::Type_ValueRemoved:
            {
                if( NodeInfo* nodeInfo = self->getNodeInfoFromNodeId(_notification->GetNodeId()))
                {
                    // Remove the value from out list
                    for( list<ValueID>::iterator it = nodeInfo->m_values.begin(); it != nodeInfo->m_values.end(); ++it )
                    {
                        if( (*it) == _notification->GetValueID() )
                        {
                            nodeInfo->m_values.erase( it );
                            break;
                        }
                    }
                }
                OpenZWave::Manager::Get()->WriteConfig(self->m_homeId);
                break;
            }
                
            case Notification::Type_ValueChanged:
            {
                // One of the node values has changed
                if( NodeInfo* nodeInfo = self->getNodeInfoFromNodeId(_notification->GetNodeId()))
                {
                    nodeInfo = nodeInfo;		// placeholder for real action
                }
                Ezwave::EventInfo event;
                event.type = _notification->GetType();
                event.data = self->getJsonFromValueID(_notification->GetValueID());
                log(
                       "Notification::Type_ValueChanged:---------------------------------------------------------\n%s\n----------------------------------------------------------------------------------------\n",
                       ABMUtils::getStyledString(event.data).c_str());
                self->pushEvent(event);
                break;
            }
                
            case Notification::Type_Group:
            {
                // One of the node's association groups has changed
                if( NodeInfo* nodeInfo = self->getNodeInfoFromNodeId(_notification->GetNodeId()))
                {
                    nodeInfo = nodeInfo;		// placeholder for real action
                }
                break;
            }
                
            case Notification::Type_NodeAdded:
            {
                // Add the new node to our list
                NodeInfo* nodeInfo = new NodeInfo();
                nodeInfo->m_homeId = _notification->GetHomeId();
                nodeInfo->m_nodeId = _notification->GetNodeId();
                nodeInfo->m_isPolled = false;
                self->m_nodes.push_back( nodeInfo );
                Ezwave::EventInfo event;
                event.type = _notification->GetType();
                event.data = self->getNodeInfo(nodeInfo->m_nodeId);
                log(
                       "Notification::Type_NodeAdded:---------------------------------------------------------\n%s\n----------------------------------------------------------------------------------------\n",
                       ABMUtils::getStyledString(event.data).c_str());
                self->pushEvent(event);
                Manager::Get()->CancelControllerCommand( _notification->GetHomeId() );
                OpenZWave::Manager::Get()->WriteConfig(self->m_homeId);
                break;
            }
                
            case Notification::Type_NodeRemoved:
            {
                // Remove the node from our list
                uint32 const homeId = _notification->GetHomeId();
                uint8 const nodeId = _notification->GetNodeId();
                Ezwave::EventInfo event;
                event.type = _notification->GetType();
                event.data = self->getNodeInfo(nodeId);
                log(
                       "Notification::Type_NodeRemoved:---------------------------------------------------------\n%s\n----------------------------------------------------------------------------------------\n",
                       ABMUtils::getStyledString(event.data).c_str());
                self->pushEvent(event);
                for( list<NodeInfo*>::iterator it = self->m_nodes.begin(); it != self->m_nodes.end(); ++it )
                {
                    NodeInfo* nodeInfo = *it;
                    if( ( nodeInfo->m_homeId == homeId ) && ( nodeInfo->m_nodeId == nodeId ) )
                    {
                        self->m_nodes.erase( it );
                        delete nodeInfo;
                        break;
                    }
                }
                OpenZWave::Manager::Get()->WriteConfig(self->m_homeId);
                break;
            }
                
            case Notification::Type_NodeEvent:
            {
                // We have received an event from the node, caused by a
                // basic_set or hail message.
                if( NodeInfo* nodeInfo = self->getNodeInfoFromNodeId(_notification->GetNodeId()))
                {
                    nodeInfo = nodeInfo;		// placeholder for real action
                }
                break;
            }
                
            case Notification::Type_PollingDisabled:
            {
                if( NodeInfo* nodeInfo = self->getNodeInfoFromNodeId(_notification->GetNodeId()))
                {
                    nodeInfo->m_isPolled = false;
                }
                break;
            }
                
            case Notification::Type_PollingEnabled:
            {
                if( NodeInfo* nodeInfo = self->getNodeInfoFromNodeId(_notification->GetNodeId()))
                {
                    nodeInfo->m_isPolled = true;
                }
                break;
            }
                
            case Notification::Type_DriverReady:
            {
                self->m_homeId = _notification->GetHomeId();
                break;
            }
                
            case Notification::Type_DriverFailed:
            {
                self->m_initFailed = true;
                self->m_initComplete = true;
                Ezwave::EventInfo event;
                event.type = _notification->GetType();
                event.data = MYJson::Value(MYJson::ValueType::nullValue);
                self->pushEvent(event);
                break;
            }
                
                
            case Notification::Type_ControllerCommand:
            {
                uint32 const homeId = _notification->GetHomeId();
                OpenZWave::Driver::ControllerState current_command_stata = (OpenZWave::Driver::ControllerState)(_notification->GetEvent());
                OpenZWave::Driver::ControllerError current_command_error = (OpenZWave::Driver::ControllerError)(_notification->GetNotification());
                Ezwave::EventInfo event;
                event.type = _notification->GetType();
                MYJson::Value data;
                event.data["command"] = OpenZWave::Manager::Get()->GetCurrentControllerCommand(homeId);
                event.data["commandName"] = Ezwave::getControllerCommandName(OpenZWave::Manager::Get()->GetCurrentControllerCommand(homeId));
                event.data["commandStata"] = current_command_stata;
                event.data["commandStataName"] = Ezwave::getControllerCommandState(current_command_stata);
                event.data["commandError"] = current_command_error;
                event.data["commandErrorName"] = Ezwave::getControllerCommandError(current_command_error);
                self->pushEvent(event);
                log(
                       "Notification::Type_ControllerCommand:---------------------------------------------------------\n%s\n----------------------------------------------------------------------------------------\n",
                       ABMUtils::getStyledString(event.data).c_str());
//                printf("[eventValue:%s,notificationValue:%s]\n",Ezwave::getControllerCommandState(eventValue).c_str(),Ezwave::getControllerCommandError(notificationValue).c_str());
                break;
            }
                
            case Notification::Type_AwakeNodesQueried:
            case Notification::Type_AllNodesQueried:
            case Notification::Type_AllNodesQueriedSomeDead:
            {
                self->m_initComplete = true;
                self->m_initFailed = false;
                Ezwave::EventInfo event;
                event.type = _notification->GetType();
                event.data = MYJson::Value(MYJson::ValueType::nullValue);
                self->pushEvent(event);
//                printf(
//                       "Notification::AllNodesQueried:---------------------------------------------------------\n%s\n----------------------------------------------------------------------------------------\n",
//                ABMUtils::getStyledString(event.data).c_str());
                break;
            }
            case Notification::Type_NodeQueriesComplete:
            {
                uint8 nodeId = _notification->GetNodeId();
                Ezwave::EventInfo event;
                event.type = _notification->GetType();
                event.data = self->getNodeInfo(nodeId);
                self->pushEvent(event);
//                printf(
//                       "Notification::Type_NodeQueriesComplete:---------------------------------------------------------\n%s\n----------------------------------------------------------------------------------------\n",
//                       ABMUtils::getStyledString(event.data).c_str());
                break;
            }
            case Notification::Type_NodeManufacturer:
            {
                uint8 nodeId = _notification->GetNodeId();
                Ezwave::EventInfo event;
                event.type = _notification->GetType();
                event.data = self->getNodeInfo(nodeId);
                self->pushEvent(event);
//                printf(
//                       "Notification::Type_NodeManufacturer:---------------------------------------------------------\n%s\n----------------------------------------------------------------------------------------\n",
//                       ABMUtils::getStyledString(event.data).c_str());
                break;
            }

            case Notification::Type_DriverReset:
            case Notification::Type_Notification:
            case Notification::Type_NodeNaming:
            case Notification::Type_NodeProtocolInfo:
            default:
            {
            }
        }
        
        self->m_criticalSection.UnLock();
    }
}

MYJson::Value Ezwave::getNodesInfo()
{
    MYJson::Value ret;
    for( list<Ezwave::NodeInfo*>::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it ){
        ret[ABMUtils::uint8toa((*it)->m_nodeId)] = getJsonFromNodeInfo(*it);
    }
    return ret;
}

//删除 除控制器节点之外其他所有节点
void Ezwave::removeOtherNode()
{
    list<Ezwave::NodeInfo*>::iterator oneNode = m_nodes.begin();
    m_nodes.clear();
    log("\nm_nodes.size() = %lu\t",m_nodes.size());
    m_nodes.push_back(*oneNode);
    log("m_nodes.size() = %lu\n",m_nodes.size());
}

MYJson::Value Ezwave::getNodeInfo(uint8 nodeId)
{
    MYJson::Value ret = MYJson::Value(MYJson::ValueType::objectValue);
    for( list<Ezwave::NodeInfo*>::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it ){
        if((*it)->m_nodeId == nodeId) return getJsonFromNodeInfo(*it);
    }
    return ret;
}

MYJson::Value Ezwave::getJsonFromNodeInfo(Ezwave::NodeInfo *nodeInfo){
    uint32 homeId = nodeInfo->m_homeId;
    uint8 nodeId = nodeInfo->m_nodeId;
    
    MYJson::Value ret;
    ret["homeId"] = nodeInfo->m_homeId;
    ret["nodeId"] = nodeInfo->m_nodeId;
    ret["isPolled"] = nodeInfo->m_isPolled;

    ret["isListeningDevice"] = Manager::Get()->IsNodeListeningDevice(homeId, nodeId);
    ret["isFrequentListeningDevice"] = Manager::Get()->IsNodeFrequentListeningDevice(homeId, nodeId);
    ret["isBeamingDevice"] = Manager::Get()->IsNodeBeamingDevice(homeId, nodeId);
    ret["isRoutingDevice"] = Manager::Get()->IsNodeRoutingDevice(homeId, nodeId);
    ret["isSecurityDevice"] = Manager::Get()->IsNodeSecurityDevice(homeId, nodeId);
    ret["isInfoReceived"] = Manager::Get()->IsNodeInfoReceived(homeId, nodeId);
    ret["isAwake"] = Manager::Get()->IsNodeAwake(homeId, nodeId);
    ret["isFailed"] = Manager::Get()->IsNodeFailed(homeId, nodeId);
    
    ret["basic"] = Manager::Get()->GetNodeBasic(homeId, nodeId);
    ret["generic"] = Manager::Get()->GetNodeGeneric(homeId, nodeId);
    ret["specific"] = Manager::Get()->GetNodeSpecific(homeId, nodeId);
    ret["version"] = Manager::Get()->GetNodeVersion(homeId, nodeId);
    ret["security"] = Manager::Get()->GetNodeSecurity(homeId, nodeId);
    ret["maxBaudRate"] = Manager::Get()->GetNodeMaxBaudRate(homeId, nodeId);
    
    ret["name"] = Manager::Get()->GetNodeName(homeId, nodeId);
    ret["location"] = Manager::Get()->GetNodeLocation(homeId, nodeId);
    ret["type"] = Manager::Get()->GetNodeType(homeId, nodeId);
    ret["manufacturerId"] = Manager::Get()->GetNodeManufacturerId(homeId, nodeId);
    ret["manufacturerName"] = Manager::Get()->GetNodeManufacturerName(homeId, nodeId);
    ret["productId"] = Manager::Get()->GetNodeProductId(homeId, nodeId);
    ret["productType"] = Manager::Get()->GetNodeProductType(homeId, nodeId);
    ret["productName"] = Manager::Get()->GetNodeProductName(homeId, nodeId);
    ret["queryStage"] = Manager::Get()->GetNodeQueryStage(homeId, nodeId);
    
    //01 - 08
    ret["isController"] = Manager::Get()->IsNodeController(homeId, nodeId);
    ret["isAddingNode"] = Manager::Get()->IsNodeAddingNode(homeId, nodeId);
    ret["deviceType"] = Manager::Get()->GetNodeDeviceType(homeId, nodeId);
    ret["deviceTypeName"] = Manager::Get()->GetNodeDeviceTypeString(homeId, nodeId);
    ret["role"] = Manager::Get()->GetNodeRole(homeId, nodeId);
    ret["roleName"] = Manager::Get()->GetNodeRoleString(homeId, nodeId);
    ret["nodeType"] = Manager::Get()->GetNodePlusType(homeId, nodeId);
    ret["nodeTypeName"] = Manager::Get()->GetNodePlusTypeString(homeId, nodeId);
    
    MYJson::Value classes;
    for( list<ValueID>::iterator it = nodeInfo->m_values.begin(); it != nodeInfo->m_values.end(); ++it )
    {
        std::string instanceValue = ABMUtils::uint8toa(it->GetInstance());
        std::string classId = ABMUtils::uint8toa(it->GetCommandClassId());
        std::string indexValue = ABMUtils::uint8toa(it->GetIndex());
        classes[instanceValue][classId][indexValue] = getJsonFromValueID(*it);
    }
    ret["classes"] = classes;
    return ret;
}

MYJson::Value Ezwave::getJsonFromValueID(OpenZWave::ValueID valueId)
{
    MYJson::Value ret;
//    ret["id"] = valueId.GetId();
    
    ret["homeId"] = valueId.GetHomeId();
    ret["nodeId"] = valueId.GetNodeId();
    ret["genre"] = valueId.GetGenre();
    ret["classId"] = valueId.GetCommandClassId();
    ret["instance"] = valueId.GetInstance();
    ret["index"] = valueId.GetIndex();
    ret["type"] = ((int)(valueId.GetType()));
//    ret["typeName"] = Ezwave::getValueTyepName(valueId.GetType());
    
    ret["label"] = Manager::Get()->GetValueLabel(valueId);
    ret["units"] = Manager::Get()->GetValueUnits(valueId);
//    ret["help"] = Manager::Get()->GetValueHelp(valueId);
    ret["min"] = Manager::Get()->GetValueMin(valueId);
    ret["max"] = Manager::Get()->GetValueMax(valueId);
    ret["readOnly"] = Manager::Get()->IsValueReadOnly(valueId);
    ret["writeOnly"] = Manager::Get()->IsValueSet(valueId);
    ret["isPolled"] = Manager::Get()->IsValuePolled(valueId);
    ret["isChangeVerified"] = Manager::Get()->GetChangeVerified(valueId);
    
    switch ((valueId).GetType()) {
        case OpenZWave::ValueID::ValueType_Bool:
        {
            bool val;
            OpenZWave::Manager::Get()->GetValueAsBool(valueId, &val);
            ret["value"] = val;
            break;
        }
        case OpenZWave::ValueID::ValueType_Byte:
        {
            uint8_t val;
            OpenZWave::Manager::Get()->GetValueAsByte(valueId, &val);
            int ival = val;
            ret["value"] = ival;
            break;
        }
        case OpenZWave::ValueID::ValueType_Decimal:
        {
            float val;
            OpenZWave::Manager::Get()->GetValueAsFloat(valueId, &val);
            ret["value"] = val;
            //uint8 precision;
            //Manager::Get()->GetValueFloatPrecision(valueId, &precision);
            //ret["precision"] = precision;
            break;
        }
        case OpenZWave::ValueID::ValueType_Int:
        {
            int32_t val;
            OpenZWave::Manager::Get()->GetValueAsInt(valueId, &val);
            ret["value"] = val;
            break;
        }
        case OpenZWave::ValueID::ValueType_List:
        {
            std::string val;
            int32_t valIndex;
            std::vector<std::string> items;
            OpenZWave::Manager::Get()->GetValueListSelection(valueId, &val);
            OpenZWave::Manager::Get()->GetValueListSelection(valueId, &valIndex);
            OpenZWave::Manager::Get()->GetValueListItems(valueId, &items);
            ret["value"] = val;
            ret["valueIndex"] = ABMUtils::itoa(valIndex);
            MYJson::Value j_items;
            for (int i = 0 ; i < items.size() ; i ++) {
                j_items.append(items.at(i));
            }
            ret["items"] = j_items;
        }
            break;
        case OpenZWave::ValueID::ValueType_Schedule:
        {
            ret["value"] = MYJson::Value(MYJson::ValueType::nullValue);
            break;
        }
        case OpenZWave::ValueID::ValueType_Short:
        {
            int16_t val;
            OpenZWave::Manager::Get()->GetValueAsShort(valueId, &val);
            ret["value"] = val;
            break;
        }
        case OpenZWave::ValueID::ValueType_String:
        {
            std::string val;
            OpenZWave::Manager::Get()->GetValueAsString(valueId, &val);
            ret["value"] = val;
            break;
        }
            
        case OpenZWave::ValueID::ValueType_Button:
        {
            ret["value"] = MYJson::Value(MYJson::ValueType::nullValue);
            break;
        }
        case OpenZWave::ValueID::ValueType_Raw:
        {
            uint8* v_uint8;
            uint8 v_length;
            Manager::Get()->GetValueAsRaw(valueId, &v_uint8, &v_length);
            std::string val= std::string((char *)v_uint8,v_length);
            ret["value"] = val;
            break;
        }
        default:
            break;
    }
    return ret;
}

std::string Ezwave::getValueTyepName(OpenZWave::ValueID::ValueType type)
{
    switch (type) {
        case OpenZWave::ValueID::ValueType_Bool:
            return "ValueType_Bool";/**< Boolean, true or false */
        case OpenZWave::ValueID::ValueType_Byte:
            return "ValueType_Byte";/**< 8-bit unsigned value */
        case OpenZWave::ValueID::ValueType_Decimal:
            return "ValueType_Decimal";/**< Represents a non-integer value as a string, to avoid floating point accuracy issues. */
        case OpenZWave::ValueID::ValueType_Int:
            return "ValueType_Int";/**< 32-bit signed value */
        case OpenZWave::ValueID::ValueType_List:
            return "ValueType_List";/**< List from which one item can be selected */
        case OpenZWave::ValueID::ValueType_Schedule:
            return "ValueType_Schedule";/**< Complex type used with the Climate Control Schedule command class */
        case OpenZWave::ValueID::ValueType_Short:
            return "ValueType_Short";/**< 16-bit signed value */
        case OpenZWave::ValueID::ValueType_String:
            return "ValueType_String";/**< Text string */
        case OpenZWave::ValueID::ValueType_Button:
            return "ValueType_Button";/**< A write-only value that is the equivalent of pressing a button to send a command to a device */
        case OpenZWave::ValueID::ValueType_Raw:
            return "ValueType_Raw";/**< A collection of bytes */
        default:
            return "ValueType_Unknow";
    }
}

std::string Ezwave::getNotificationTypeName(int type)
{
    switch (type) {
        case OpenZWave::Notification::NotificationType::Type_ValueAdded:
            return "Type_ValueAdded";//0 A new node value has been added to OpenZWave's list. These notifications occur after a node has been discovered, and details of its command classes have been received.  Each command class may generate one or more values depending on the complexity of the item being represented.
        case OpenZWave::Notification::NotificationType::Type_ValueRemoved:
            return "Type_ValueRemoved";//1 A node value has been removed from OpenZWave's list.  This only occurs when a node is removed.
        case OpenZWave::Notification::NotificationType::Type_ValueChanged:
            return "Type_ValueChanged";//2 A node value has been updated from the Z-Wave network and it is different from the previous value.
        case OpenZWave::Notification::NotificationType::Type_ValueRefreshed:
            return "Type_ValueRefreshed";//3  A node value has been updated from the Z-Wave network.
        case OpenZWave::Notification::NotificationType::Type_Group:
            return "Type_Group";//4 The associations for the node have changed. The application should rebuild any group information it holds about the node.
        case OpenZWave::Notification::NotificationType::Type_NodeNew:
            return "Type_NodeNew";//5 A new node has been found (not already stored in zwcfg*.xml file)
        case OpenZWave::Notification::NotificationType::Type_NodeAdded:
            return "Type_NodeAdded";//6  A new node has been added to OpenZWave's list.  This may be due to a device being added to the Z-Wave network, or because the application is initializing itself.
        case OpenZWave::Notification::NotificationType::Type_NodeRemoved:
            return "Type_NodeRemoved";//7  A node has been removed from OpenZWave's list.  This may be due to a device being removed from the Z-Wave network, or because the application is closing.
        case OpenZWave::Notification::NotificationType::Type_NodeProtocolInfo:
            return "Type_NodeProtocolInfo";//8  Basic node information has been receievd, such as whether the node is a listening device, a routing device and its baud rate and basic, generic and specific types. It is after this notification that you can call Manager::GetNodeType to obtain a label containing the device description.
        case OpenZWave::Notification::NotificationType::Type_NodeNaming:
            return "Type_NodeNaming";//9  One of the node names has changed (name, manufacturer, product).
        case OpenZWave::Notification::NotificationType::Type_NodeEvent:
            return "Type_NodeEvent";//10  A node has triggered an event.  This is commonly caused when a node sends a Basic_Set command to the controller.  The event value is stored in the notification.
        case OpenZWave::Notification::NotificationType::Type_PollingDisabled:
            return "Type_PollingDisabled";//11  Polling of a node has been successfully turned off by a call to Manager::DisablePoll
        case OpenZWave::Notification::NotificationType::Type_PollingEnabled:
            return "Type_PollingEnabled";//12  Polling of a node has been successfully turned on by a call to Manager::EnablePoll
        case OpenZWave::Notification::NotificationType::Type_SceneEvent:
            return "Type_SceneEvent";//13  Scene Activation Set received
        case OpenZWave::Notification::NotificationType::Type_CreateButton:
            return "Type_CreateButton";//14  Handheld controller button event created
        case OpenZWave::Notification::NotificationType::Type_DeleteButton:
            return "Type_DeleteButton";//15   Handheld controller button event deleted
        case OpenZWave::Notification::NotificationType::Type_ButtonOn:
            return "Type_ButtonOn";//16   Handheld controller button on pressed event
        case OpenZWave::Notification::NotificationType::Type_ButtonOff:
            return "Type_ButtonOff";//17   Handheld controller button off pressed event
        case OpenZWave::Notification::NotificationType::Type_DriverReady:
            return "Type_DriverReady";//18  A driver for a PC Z-Wave controller has been added and is ready to use.  The notification will contain the controller's Home ID, which is needed to call most of the Manager methods.
        case OpenZWave::Notification::NotificationType::Type_DriverFailed:
            return "Type_DriverFailed";//19  Driver failed to load
        case OpenZWave::Notification::NotificationType::Type_DriverReset:
            return "Type_DriverReset";//20   All nodes and values for this driver have been removed.  This is sent instead of potentially hundreds of individual node and value notifications.
        case OpenZWave::Notification::NotificationType::Type_EssentialNodeQueriesComplete:
            return "Type_EssentialNodeQueriesComplete";//21  The queries on a node that are essential to its operation have been completed. The node can now handle incoming messages.
        case OpenZWave::Notification::NotificationType::Type_NodeQueriesComplete:
            return "Type_NodeQueriesComplete";//22  All the initialisation queries on a node have been completed.
        case OpenZWave::Notification::NotificationType::Type_AwakeNodesQueried:
            return "Type_AwakeNodesQueried";//23   All awake nodes have been queried, so client application can expected complete data for these nodes.
        case OpenZWave::Notification::NotificationType::Type_AllNodesQueriedSomeDead:
            return "Type_AllNodesQueriedSomeDead";//24   All nodes have been queried but some dead nodes found.
        case OpenZWave::Notification::NotificationType::Type_AllNodesQueried:
            return "Type_AllNodesQueried";//25   All nodes have been queried, so client application can expected complete data.
        case OpenZWave::Notification::NotificationType::Type_Notification:
            return "Type_Notification";//26  An error has occured that we need to report.
        case OpenZWave::Notification::NotificationType::Type_DriverRemoved:
            return "Type_DriverRemoved";//27   The Driver is being removed. (either due to Error or by request) Do Not Call Any Driver Related Methods after recieving this call
        case OpenZWave::Notification::NotificationType::Type_ControllerCommand:
            return "Type_ControllerCommand";//28
        case OpenZWave::Notification::NotificationType::Type_NodeReset:
            return "Type_NodeReset";//29				/**< The Device has been reset and thus removed from the NodeList in OZW * /
        case OpenZWave::Notification::NotificationType::Type_NodeManufacturer:
            return "Type_NodeManufacturer";//30
        default:
            return "NotificationType_Unknow";
    }
}

std::string Ezwave::getControllerCommandState(uint8 state)
{
    switch (state) {
        case OpenZWave::Driver::ControllerState::ControllerState_Normal:
            return "ControllerState_Normal";/**< No command in progress. */
        case OpenZWave::Driver::ControllerState::ControllerState_Starting:
            return "ControllerState_Starting";/**< The command is starting. */
        case OpenZWave::Driver::ControllerState::ControllerState_Cancel:
            return "ControllerState_Cancel";/**< The command was canceled. */
        case OpenZWave::Driver::ControllerState::ControllerState_Error:
            return "ControllerState_Error";/**< Command invocation had error(s) and was aborted */
        case OpenZWave::Driver::ControllerState::ControllerState_Waiting:
            return "ControllerState_Waiting";/**< Controller is waiting for a user action. */
        case OpenZWave::Driver::ControllerState::ControllerState_Sleeping:
            return "ControllerStControllerState_Sleepingate_Starting";/**< Controller command is on a sleep queue wait for device. */
        case OpenZWave::Driver::ControllerState::ControllerState_InProgress:
            return "ControllerState_InProgress";/**< The controller is communicating with the other device to carry out the command. */
        case OpenZWave::Driver::ControllerState::ControllerState_Completed:
           return "ControllerState_Completed";/**< The command has completed successfully. */
        case OpenZWave::Driver::ControllerState::ControllerState_Failed:
           return "ControllerState_Failed";/**< The command has failed. */
        case OpenZWave::Driver::ControllerState::ControllerState_NodeOK:
            return "ControllerState_NodeOK";/**< Used only with ControllerCommand_HasNodeFailed to indicate that the controller thinks the node is OK. */
        case OpenZWave::Driver::ControllerState::ControllerState_NodeFailed:
           return "ControllerState_NodeFailed";/**< Used only with ControllerCommand_HasNodeFailed to indicate that the controller thinks the node has failed. */
        default:
            return "ControllerState_Unknow";
    }
}

std::string Ezwave::getControllerCommandError(uint8 error)
{
    switch (error) {
        case OpenZWave::Driver::ControllerError::ControllerError_None:
            return "ControllerError_None";
        case OpenZWave::Driver::ControllerError::ControllerError_ButtonNotFound:
            return "ControllerError_ButtonNotFound";/**< Button */
        case OpenZWave::Driver::ControllerError::ControllerError_NodeNotFound:
            return "ControllerError_NodeNotFound";/**< Button */
        case OpenZWave::Driver::ControllerError::ControllerError_NotBridge:
            return "ControllerError_NotBridge";/**< Button */
        case OpenZWave::Driver::ControllerError::ControllerError_NotSUC:
            return "ControllerError_NotSUC";/**< CreateNewPrimary */
        case OpenZWave::Driver::ControllerError::ControllerError_NotSecondary:
            return "ControllerError_NotSecondary";/**< CreateNewPrimary */
        case OpenZWave::Driver::ControllerError::ControllerError_NotPrimary:
            return "ControllerError_NotPrimary";/**< RemoveFailedNode, AddNodeToNetwork */
        case OpenZWave::Driver::ControllerError::ControllerError_IsPrimary:
            return "ControllerError_IsPrimary";/**< ReceiveConfiguration */
        case OpenZWave::Driver::ControllerError::ControllerError_NotFound:
            return "ControllerError_NotFound";/**< RemoveFailedNode */
        case OpenZWave::Driver::ControllerError::ControllerError_Busy:
            return "ControllerError_Busy";/**< RemoveFailedNode, RequestNetworkUpdate */
        case OpenZWave::Driver::ControllerError::ControllerError_Failed:
            return "ControllerError_Failed";/**< RemoveFailedNode, RequestNetworkUpdate */
        case OpenZWave::Driver::ControllerError::ControllerError_Disabled:
            return "ControllerError_Disabled";/**< RequestNetworkUpdate error */
        case OpenZWave::Driver::ControllerError::ControllerError_Overflow:
            return "ControllerError_Overflow";/**< RequestNetworkUpdate error */
        default:
            return "ControllerState_Unknow";
    }
}

std::string Ezwave::getControllerCommandName(uint8 command)
{
    switch (command) {
        case OpenZWave::Driver::ControllerCommand::ControllerCommand_None:
            return "ControllerCommand_None";/**< No command. */
        case OpenZWave::Driver::ControllerCommand::ControllerCommand_AddDevice:
            return "ControllerCommand_AddDevice";/**< Add a new device or controller to the Z-Wave network. */
        case OpenZWave::Driver::ControllerCommand::ControllerCommand_CreateNewPrimary:
            return "ControllerCommand_CreateNewPrimary";/**< Add a new controller to the Z-Wave network. Used when old primary fails. Requires SUC. */
        case OpenZWave::Driver::ControllerCommand::ControllerCommand_ReceiveConfiguration:
            return "ControllerCommand_ReceiveConfiguration";/**< Receive Z-Wave network configuration information from another controller. */
        case OpenZWave::Driver::ControllerCommand::ControllerCommand_RemoveDevice:
            return "ControllerCommand_RemoveDevice";/**< Remove a device or controller from the Z-Wave network. */
        case OpenZWave::Driver::ControllerCommand::ControllerCommand_RemoveFailedNode:
            return "ControllerCommand_RemoveFailedNode";/**< Move a node to the controller's failed nodes list. This command will only work if the node cannot respond. */
        case OpenZWave::Driver::ControllerCommand::ControllerCommand_HasNodeFailed:
            return "ControllerCommand_HasNodeFailed";/**< Check whether a node is in the controller's failed nodes list. */
        case OpenZWave::Driver::ControllerCommand::ControllerCommand_ReplaceFailedNode:
            return "ControllerCommand_ReplaceFailedNode";/**< Replace a non-responding node with another. The node must be in the controller's list of failed nodes for this command to succeed. */
        case OpenZWave::Driver::ControllerCommand::ControllerCommand_TransferPrimaryRole:
            return "ControllerCommand_TransferPrimaryRole";/**< Make a different controller the primary. */
        case OpenZWave::Driver::ControllerCommand::ControllerCommand_RequestNetworkUpdate:
            return "ControllerCommand_RequestNetworkUpdate";/**< Request network information from the SUC/SIS. */
        case OpenZWave::Driver::ControllerCommand::ControllerCommand_RequestNodeNeighborUpdate:
            return "ControllerCommand_RequestNodeNeighborUpdate";/**< Get a node to rebuild its neighbour list.  This method also does RequestNodeNeighbors */
        case OpenZWave::Driver::ControllerCommand::ControllerCommand_AssignReturnRoute:
            return "ControllerCommand_AssignReturnRoute";/**< Assign a network return routes to a device. */
        case OpenZWave::Driver::ControllerCommand::ControllerCommand_DeleteAllReturnRoutes:
            return "ControllerCommand_DeleteAllReturnRoutes";/**< Delete all return routes from a device. */
        case OpenZWave::Driver::ControllerCommand::ControllerCommand_SendNodeInformation:
            return "ControllerCommand_SendNodeInformation";/**< Send a node information frame */
        case OpenZWave::Driver::ControllerCommand::ControllerCommand_ReplicationSend:
            return "ControllerCommand_ReplicationSend";/**< Send information from primary to secondary */
        case OpenZWave::Driver::ControllerCommand::ControllerCommand_CreateButton:
            return "ControllerCommand_CreateButton";/**< Create an id that tracks handheld button presses */
        case OpenZWave::Driver::ControllerCommand::ControllerCommand_DeleteButton:
            return "ControllerCommand_DeleteButton";/**< Delete id that tracks handheld button presses */
        default:
            return "ControllerCommand_Unknow";
    }
};

bool Ezwave::setValue(std::string nodeIdStr,std::string instanceStr,std::string classIdStr,std::string indexStr,std::string valueStr)
{
    
    uint8_t nodeid = atoi(nodeIdStr.c_str());
    uint8_t comclass = atoi(classIdStr.c_str());
    uint8_t index = atoi(indexStr.c_str());
    uint8_t instance = atoi(instanceStr.c_str());
    
    fprintf(stderr,"LYLOG(v4.0) %s (line:%d) ==> %s():[  setValue(%s,%s,%s,%s,%s); ]\n",__FILE__,__LINE__,__FUNCTION__,nodeIdStr.c_str(),instanceStr.c_str(),classIdStr.c_str(),indexStr.c_str(),valueStr.c_str());
    
    NodeInfo *node;
    std::list<OpenZWave::ValueID>::iterator vit;
    
    if ((node = getNodeInfoFromNodeId(nodeid))) {
        for (vit = node->m_values.begin(); vit != node->m_values.end(); ++vit) {
            if (((*vit).GetCommandClassId() == comclass) &&
                ((*vit).GetIndex() == index) &&
                ((*vit).GetInstance() == instance)) {
                
                switch ((*vit).GetType()) {
                    case OpenZWave::ValueID::ValueType_Bool:
                    {
                        bool val;
                        if(valueStr == "true") val = true;
                        else val = false;
                        OpenZWave::Manager::Get()->SetValue(*vit, val);
                        break;
                    }
                    case OpenZWave::ValueID::ValueType_Byte:
                    {
                        uint8_t val = atoi(valueStr.c_str());
                        OpenZWave::Manager::Get()->SetValue(*vit, val);
                        break;
                    }
                    case OpenZWave::ValueID::ValueType_Decimal:
                    {
                        float val = atof(valueStr.c_str());
                        OpenZWave::Manager::Get()->SetValue(*vit, val);
                        break;
                    }
                    case OpenZWave::ValueID::ValueType_Int:
                    {
                        int32_t val = atof(valueStr.c_str());
                        OpenZWave::Manager::Get()->SetValue(*vit, val);
                        break;
                    }
                    case OpenZWave::ValueID::ValueType_Short:
                    {
                        int16_t val = atof(valueStr.c_str());
                        OpenZWave::Manager::Get()->SetValue(*vit, val);
                        break;
                    }
                    case OpenZWave::ValueID::ValueType_String:
                    {
                        std::string val = valueStr;
                        OpenZWave::Manager::Get()->SetValue(*vit, val);
                        break;
                    }
                    case OpenZWave::ValueID::ValueType_List:
                    {
                        std::string val = valueStr;
                        OpenZWave::Manager::Get()->SetValueListSelection(*vit, val);
                    }
                        break;
                    case OpenZWave::ValueID::ValueType_Schedule:
                    {
                        //                        TODO;
                        break;
                    }
                        
                    case OpenZWave::ValueID::ValueType_Button:
                    {
                        bool val;
                        if(valueStr == "true") val = true;
                        else val = false;
                        if (val) {
                            OpenZWave::Manager::Get()->PressButton(*vit);
                        }else{
                            OpenZWave::Manager::Get()->ReleaseButton(*vit);
                        }
                        break;
                    }
                    case OpenZWave::ValueID::ValueType_Raw:
                    {
                        //                        TODO;
                        break;
                    }
                }
            }
        }
    }
    return true;
}

bool Ezwave::refreshValue(std::string nodeIdStr,std::string instanceStr,std::string classIdStr,std::string indexStr)
{
    uint8_t nodeid = atoi(nodeIdStr.c_str());
    uint8_t comclass = atoi(classIdStr.c_str());
    uint8_t index = atoi(indexStr.c_str());
    uint8_t instance = atoi(instanceStr.c_str());
    NodeInfo *node;
    std::list<OpenZWave::ValueID>::iterator vit;
    
    if ((node = getNodeInfoFromNodeId(nodeid))) {
        for (vit = node->m_values.begin(); vit != node->m_values.end(); ++vit) {
            if (((*vit).GetCommandClassId() == comclass) &&
                ((*vit).GetIndex() == index) &&
                ((*vit).GetInstance() == instance)) {
                OpenZWave::Manager::Get()->RefreshValue((*vit));
            }
        }
    }
    return true;
}


bool Ezwave::refreshAllValue()
{
    NodeInfo *node;
    std::list<OpenZWave::ValueID>::iterator vit;
    for( std::list<NodeInfo*>::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it )
    {
        node = *it;
        for (vit = node->m_values.begin(); vit != node->m_values.end(); ++vit) {
            OpenZWave::Manager::Get()->RefreshValue((*vit));
        }
    }
    return true;
}

bool Ezwave::refreshNodeValue(std::string nodeIdStr)
{
    uint8_t nodeid = atoi(nodeIdStr.c_str());
    
    NodeInfo *node;
    std::list<OpenZWave::ValueID>::iterator vit;
    
    if ((node = getNodeInfoFromNodeId(nodeid))) {
        for (vit = node->m_values.begin(); vit != node->m_values.end(); ++vit) {
            OpenZWave::Manager::Get()->RefreshValue((*vit));
        }
    }
    return true;
}

bool Ezwave::getChangeVerified(std::string nodeIdStr,std::string instanceStr,std::string classIdStr,std::string indexStr)
{
    uint8_t nodeid = atoi(nodeIdStr.c_str());
    uint8_t comclass = atoi(classIdStr.c_str());
    uint8_t index = atoi(indexStr.c_str());
    uint8_t instance = atoi(instanceStr.c_str());
    NodeInfo *node;
    std::list<OpenZWave::ValueID>::iterator vit;
    
    if ((node = getNodeInfoFromNodeId(nodeid))) {
        for (vit = node->m_values.begin(); vit != node->m_values.end(); ++vit) {
            if (((*vit).GetCommandClassId() == comclass) &&
                ((*vit).GetIndex() == index) &&
                ((*vit).GetInstance() == instance)) {
                return OpenZWave::Manager::Get()->GetChangeVerified((*vit));
            }
        }
    }
    return false;
}

bool Ezwave::setChangeVerified(std::string nodeIdStr,std::string instanceStr,std::string classIdStr,std::string indexStr)
{
    uint8_t nodeid = atoi(nodeIdStr.c_str());
    uint8_t comclass = atoi(classIdStr.c_str());
    uint8_t index = atoi(indexStr.c_str());
    uint8_t instance = atoi(instanceStr.c_str());
    NodeInfo *node;
    std::list<OpenZWave::ValueID>::iterator vit;
    
    if ((node = getNodeInfoFromNodeId(nodeid))) {
        for (vit = node->m_values.begin(); vit != node->m_values.end(); ++vit) {
            if (((*vit).GetCommandClassId() == comclass) &&
                ((*vit).GetIndex() == index) &&
                ((*vit).GetInstance() == instance)) {
                OpenZWave::Manager::Get()->SetChangeVerified((*vit),true);
            }
        }
    }
    return true;
}

void Ezwave::destory(){
    if (s_Options) {
        Options::Destroy();
        s_Options = NULL;
    }
    if (s_Manager) {
        Manager::Destroy();
        s_Options = NULL;
    }
}

void Ezwave::pushEvent(Ezwave::EventInfo event)
{
//    if(m_initComplete){
//        m_evnetMutex.Lock();
//        m_events.push_back(event);
//        m_evnetMutex.UnLock();
//    }
    m_evnetMutex.Lock();
    m_events.push_back(event);
    m_evnetMutex.UnLock();
}

void Ezwave::update()
{
    if(m_events.size()>0){
        log("Ezwave::update()");
        std::vector<Ezwave::EventInfo> events;
        m_evnetMutex.Lock();
        events = m_events;
        m_events.clear();
        m_evnetMutex.UnLock();
        for (int i = 0 ; i < events.size() ; ++i) {
            AccountData::getInstance()->zwaveEventCallback(events.at(i));
        }
    }
}

bool Ezwave::reset()
{
    m_initComplete = false;
    m_initFailed = false;
    Ez::EzDir dir = Ez::listDir(m_zwavePath);
    for (std::list<Ez::EzFile>::iterator it = dir.list.begin() ; it != dir.list.end() ; ++it ) {
        Ez::removeFile(it->path);
    }
    OpenZWave::Manager::Get()->ResetController(m_homeId);
    return true;
}

bool Ezwave::writeConfig()
{
    OpenZWave::Manager::Get()->WriteConfig(m_homeId);
    return true;
}


bool Ezwave::addNode()
{
    //if(!m_initComplete||m_initFailed) return false;
    if(OpenZWave::Manager::Get()->GetCurrentControllerCommand(m_homeId) != OpenZWave::Driver::ControllerCommand::ControllerCommand_None)
    {
        return false;
    }
    return OpenZWave::Manager::Get()->AddNode(m_homeId);
}

bool Ezwave::removeNode()
{
    //if(!m_initComplete||m_initFailed) return false;
    if(OpenZWave::Manager::Get()->GetCurrentControllerCommand(m_homeId) != OpenZWave::Driver::ControllerCommand::ControllerCommand_None)
    {
        return false;
    }
    return OpenZWave::Manager::Get()->RemoveNode(m_homeId);
}

bool Ezwave::hasNodeFailed(std::string nodeIdStr)
{
    //if(!m_initComplete||m_initFailed) return false;
    uint8_t nodeId = atoi(nodeIdStr.c_str());
    if(nodeId < 1 || nodeId > 254) return false;
    if(OpenZWave::Manager::Get()->GetCurrentControllerCommand(m_homeId) != OpenZWave::Driver::ControllerCommand::ControllerCommand_None)
    {
        return false;
    }
    return OpenZWave::Manager::Get()->HasNodeFailed(m_homeId, nodeId);
}

bool Ezwave::requestNetworkUpdate()
{
    //if(!m_initComplete||m_initFailed) return false;
    if(OpenZWave::Manager::Get()->GetCurrentControllerCommand(m_homeId) != OpenZWave::Driver::ControllerCommand::ControllerCommand_None)
    {
        return false;
    }
    
    for( list<Ezwave::NodeInfo*>::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it ){
        OpenZWave::Manager::Get()->RequestNetworkUpdate(m_homeId, (*it)->m_nodeId);
    }
    
    return true;
}

bool Ezwave::setConfigParam(std::string nodeIdStr, std::string paramStr, std::string valueStr, std::string sizeStr)
{
    uint8 nodeId = atoi(nodeIdStr.c_str());
    uint8 param = atoi(paramStr.c_str());
    uint8 value = atoi(valueStr.c_str());
    uint8 size = atoi(sizeStr.c_str());
    fprintf(stderr,"LYLOG(v4.0) %s (line:%d) ==> %s():[  setConfigParam(%s,%s,%s,%s); ]\n",__FILE__,__LINE__,__FUNCTION__,nodeIdStr.c_str(),paramStr.c_str(),valueStr.c_str(),sizeStr.c_str());
    //if(!m_initComplete||m_initFailed) return false;
    if(nodeId < 1 || nodeId > 254) return false;
    return OpenZWave::Manager::Get()->SetConfigParam(m_homeId, nodeId, param, value,size);
}

bool Ezwave::requestAllConfigParams( std::string nodeIdStr )
{
    //if(!m_initComplete||m_initFailed) return false;
    uint8_t nodeId = atoi(nodeIdStr.c_str());
    if(nodeId < 1 || nodeId > 254) return false;
    OpenZWave::Manager::Get()->RequestAllConfigParams(m_homeId, nodeId);
    return true;
}

bool Ezwave::removeFailedNode(std::string nodeIdStr)
{
    //if(!m_initComplete||m_initFailed) return false;
    uint8_t nodeId = atoi(nodeIdStr.c_str());
    if(nodeId < 1 || nodeId > 254) return false;
    if(OpenZWave::Manager::Get()->GetCurrentControllerCommand(m_homeId) != OpenZWave::Driver::ControllerCommand::ControllerCommand_None)
    {
        return false;
    }
    return OpenZWave::Manager::Get()->RemoveFailedNode(m_homeId, nodeId);
}

bool Ezwave::cancelControllerCommand()
{
    //if(!m_initComplete||m_initFailed) return false;
    if(OpenZWave::Manager::Get()->GetCurrentControllerCommand(m_homeId) == OpenZWave::Driver::ControllerCommand::ControllerCommand_None)
    {
        return true;
    }
    return OpenZWave::Manager::Get()->CancelControllerCommand(m_homeId);
}

EZ_END
#endif
